package org.syncsoft.overalt.common.json;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Abstract class to IJsonConverter
 * 
 * @author tptfc
 *
 * @param <T>
 */
public abstract class AbstractJsonConverter<T> implements IJsonConverter<T> {
	/* (non-Javadoc)
	 * @see org.syncsoft.overalt.common.json.IJsonConverter#converter(java.lang.String)
	 */
	public T converter(String json) {
		JSonTree jsonTree = null;

		if (json != null && json.contains("{")) {
			jsonTree = new JSonTree();
			
			StringBuilder builder = new StringBuilder(json.trim());
			
			boolean isString = false;
			boolean hasBuffer = false;
			
			StringBuilder buffer = new StringBuilder();
			
			for (int i = 0; i < builder.length(); i++) {
				char c = builder.charAt(i);
				
				if (c == '"') {
					isString = !isString;
					continue;
				}
				
				if (c == '{' && !isString) {
					jsonTree.openEntity();
					continue;
				}
				
				if (c == '[' && !isString) {
					continue;
				}
				
				if (c == ':' && !isString && hasBuffer) {
					jsonTree.addKey(getCorrectValue(buffer.toString()));
					clearBuffer(buffer);
					hasBuffer = false;
					continue;
				}
				
				if ((c == ']' || c == '}' || c == ',') && !isString) {
					if (hasBuffer) {
						jsonTree.addParameter(getCorrectValue(buffer.toString()));
						clearBuffer(buffer);
						hasBuffer = false;
					}
					
					if (c == '}') {
						jsonTree.closeEntity();
					}
					
					continue;
				}
				
				buffer.append(c);
				hasBuffer = true;
			}
		}
		
		return converter(jsonTree);
	}
	
	/**
	 * @precondition buffer != null
	 * @postcondition buffer.isEmpty = true
	 * @param buffer
	 */
	private void clearBuffer(StringBuilder buffer) {
		if (buffer != null) {
			buffer.replace(0, buffer.length(), "");
		}
	}
	
	/**
	 * 
	 * @param value
	 * @return
	 */
	private String getCorrectValue(String value) {
		if (value != null && value.startsWith("\"") && value.endsWith("\"")) {
			value = value.substring(1, value.length() - 1);
		}
		
		return value;
	}
	
	/**
	 * @precondition entity and jsonTree aren't null
	 * @postcondition entity is populated from jsonTree parameters
	 * 
	 * @param object
	 * @param jsonTree
	 */
	protected void populeEntity(Object entity, JSonTree jsonTree) {
		if (entity != null && jsonTree != null) {
			for (Method method : entity.getClass().getMethods()) {
				boolean isAccessible = method.getModifiers() == Modifier.PUBLIC;
				boolean isSetMethod = method.getName().startsWith("set");
				boolean hasJustOneParameter = method.getParameterTypes() != null && method.getParameterTypes().length == 1;
				
				if (isAccessible && isSetMethod && hasJustOneParameter) {
					StringBuilder builder = new StringBuilder();
					builder.append(method.getName().replace("set", ""));
					builder.replace(0, 1, builder.substring(0 , 1).toLowerCase());
					String value = jsonTree.getParameterValue(builder.toString());
					
					if (value != null) {
						populeMethod(entity, method, value);
					}
				}
			}
		}
	}

	/**
	 * 
	 * @param entity
	 * @param method
	 * @param value
	 */
	private void populeMethod(Object entity, Method method, String value) {
		try {
			Class<?> clazz = method.getParameterTypes()[0];
			if ((Integer.class.isAssignableFrom(clazz) || int.class.isAssignableFrom(clazz))) {
				method.invoke(entity, Integer.parseInt(value));
			} else if (String.class.equals(method.getParameterTypes()[0])) {
				method.invoke(entity, value);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * @pattern template method
	 * @precondition jsonTree != null
	 * 
	 * @param jSonTree
	 * @return
	 */
	protected abstract T converter(JSonTree jsonTree);
}
